﻿////////////////////////////////////////////
/// 用于发送 http请求和 websocket数据传输的类
////////////////////////////////////////////
#pragma once
#include <QNetWorkAccessManager>
#include <QNetworkReply>
#include <QObject>
#include <QProtobufSerializer>
#include <QString>
#include <QUrl>
#include <QWebSocket>
#include "../Debug.h"
#include "../Model/Data.h"
#include "notify.qpb.h"

// #include "../Model/DataCenter.h"
using namespace model;
// 前置声明解决循环包含问题
namespace model
{
class DataCenter;
}

class NetClient : public QObject
{
    Q_OBJECT
private:
    /* 网络需要用的静态常量 */
    const inline static QString HTTP_URL                   = "http://127.0.0.1:9000";                                       // HTTP请求的URL
    const inline static QString WS_URL                     = "ws://127.0.0.1:9001/ws";                                      // WebSocket请求的URL
    const inline static QUrl    PING_URL                   = QUrl(HTTP_URL + "/ping");                                      // Ping请求的URL
    const inline static QUrl    GET_USER_INFO_URL          = QUrl(HTTP_URL + "/service/user/get_user_info");                // 获取用户信息的URL
    const inline static QUrl    GET_FRIEND_LIST_URL        = QUrl(HTTP_URL + "/service/friend/get_friend_list");            // 获取好友列表的URL
    const inline static QUrl    GET_SESSION_LIST_URL       = QUrl(HTTP_URL + "/service/friend/get_chat_session_list");      // 获取会话列表的URL
    const inline static QUrl    GET_FRIEND_APPLY_LIST_URL  = QUrl(HTTP_URL + "/service/friend/get_pending_friend_events");  // 获取好友申请列表的URL
    const inline static QUrl    GET_RECENT_MSG_URL         = QUrl(HTTP_URL + "/service/message_storage/get_recent");        // 获取最近消息的URL
    const inline static QUrl    SEND_MSG_URL               = QUrl(HTTP_URL + "/service/message_transmit/new_message");      // 发送消息的URL
    const inline static QUrl    SET_NICK_NAME_URL          = QUrl(HTTP_URL + "/service/user/set_nickname");                 // 设置昵称的URL
    const inline static QUrl    SET_DESCRIPTION_URL        = QUrl(HTTP_URL + "/service/user/set_description");              // 设置个性签名的URL
    const inline static QUrl    GET_PHONE_VERIFY_CODE_URL  = QUrl(HTTP_URL + "/service/user/get_phone_verify_code");        // 获取手机验证码的URL
    const inline static QUrl    SET_PHONE_NUM_URL          = QUrl(HTTP_URL + "/service/user/set_phone");                    // 重新设置手机号的URL
    const inline static QUrl    SET_AVATAR_URL             = QUrl(HTTP_URL + "/service/user/set_avatar");                   // 设置用户头像的URL
    const inline static QUrl    DEL_FRIEND_URL             = QUrl(HTTP_URL + "/service/friend/remove_friend");              // 删除好友的URL
    const inline static QUrl    ADD_FRIEND_URL             = QUrl(HTTP_URL + "/service/friend/add_friend_apply");           // 发送添加好友的URL
    const inline static QUrl    PROCESS_FRIEND_APPLY_URL   = QUrl(HTTP_URL + "/service/friend/add_friend_process");         // 好友申请处理的URL(接收和不接受都用这个)
    const inline static QUrl    CREATE_GROUP_URL           = QUrl(HTTP_URL + "/service/friend/create_chat_session");        // 创建群组的URL
    const inline static QUrl    GET_GROUP_MEMBER_LIST_URL  = QUrl(HTTP_URL + "/service/friend/get_chat_session_member");    // 获取群组成员列表的URL
    const inline static QUrl    SEARCH_USER_URL            = QUrl(HTTP_URL + "/service/friend/search_friend");              // 搜索好友的URL
    const inline static QUrl    SEARCH_HISTORY_URL         = QUrl(HTTP_URL + "/service/message_storage/search_history");    // 搜索历史消息的URL, 根据关键字获取
    const inline static QUrl    SEARCH_HISTORY_URL_BY_TIME = QUrl(HTTP_URL + "/service/message_storage/get_history");       // 搜索历史消息的URL, 根据时间获取
    const inline static QUrl    USER_LOGIN_URL             = QUrl(HTTP_URL + "/service/user/username_login");               // 使用用户名密码登录的URL
    const inline static QUrl    USER_REGISTER_URL          = QUrl(HTTP_URL + "/service/user/username_register");            // 使用用户名密码注册的URL
    const inline static QUrl    PHONE_LOGIN_URL            = QUrl(HTTP_URL + "/service/user/phone_login");                  // 使用手机号码登录的URL
    const inline static QUrl    PHONE_REGISTER_URL         = QUrl(HTTP_URL + "/service/user/phone_register");               // 使用手机号码注册的URL
    const inline static QUrl    GET_SINGLE_FILE_URL        = QUrl(HTTP_URL + "/service/file/get_single_file");              // 获取单个文件的URL
    const inline static QUrl    SPEECH_RECOGNITION_URL     = QUrl(HTTP_URL + "/service/speech/recognition");                // 语音识别的URL
public:
    /* 工具方法 */
    NetClient(model::DataCenter* data_center);
    ~NetClient();
    static QString make_id();
public:
    /* 网络相关函数 */
    void ping();
    void send_authentication();
    void get_myself_info(const QString& login_session_id);
    void get_friend_list(const QString& login_session_id);
    void get_chat_session_list(const QString& login_seesion_id);
    void get_friend_apply_list(const QString& login_seesion_id);
    void get_recent_messages(const QString& login_session_id, const QString& chat_session_id, int cnt, bool update_ui);
    void send_message(const QString& login_session_id, const QString& chat_session_id,
                      MessageType type, const QByteArray& content, const QString& extra_info);
    void set_user_nick_name(const QString& login_session_id, const QString& nick_name);
    void set_user_description(const QString& login_session_id, const QString& desc);
    void set_user_phone_num(const QString& login_session_id, const QString& phone_num, const QString& phone_verify_code_id, const QString& phone_verify_code);
    void get_phone_verify_code(const QString& phone_num);
    void set_user_avatar(const QString& login_session_id, const QByteArray& image);
    void del_friend(const QString& login_session_id, const QString& user_id);
    void add_friend(const QString& login_session_id, const QString& user_id);
    void accept_friend_apply(const QString& login_session_id, const QString& user_id);
    void reject_friend_apply(const QString& login_session_id, const QString& user_id);
    void create_group(const QString& login_session_id, const QList<QString>& user_ids);
    void get_group_member_list(const QString& login_session_id, const QString& chat_session_id);
    void search_user(const QString& login_session_id, const QString& search_key);
    void search_history_message(const QString& login_session_id, const QString& chat_session_id, const QString& search_key);
    void search_history_message_by_time(const QString& login_session_id, const QString& chat_session_id, qint64 start_time, qint64 end_time);
    void user_login(const QString& user_name, const QString& pwd);
    void user_register(const QString& user_name, const QString& pwd);
    void phone_login(const QString& phone_num, const QString& verify_code);
    void phone_register(const QString& phone_num, const QString& verify_code);
    void get_single_file(const QString& login_session_id, const QString& file_id);
    void speech_recognition(const QString& login_session_id, const QByteArray& data);
    /* 处理WEBSOCKET请求 */
    bool init_web_socket();
    void handle_ws_binary_response(std::shared_ptr<chat::NotifyMessage> response);
    void handle_ws_message(const Message& message);
    void handle_ws_remove_friend(const QString& user_id);
    void handle_ws_add_friend(const UserInfo& user_id);
    void handle_ws_add_friend_process(const UserInfo& user_info, bool agree);
    void handle_ws_creat_group(const ChatSession& chat_session);
private:
    /* 内部调用函数封装 */
    QNetworkReply* send_http_request(const QUrl& url, const QByteArray& body);
    template <typename T>
    std::shared_ptr<T> handle_http_response(QNetworkReply* response);
    template <typename T>
    std::shared_ptr<T> handle_http_response(QNetworkReply* response, bool* ok, QString* reason);
private:
    QNetworkAccessManager http_client;  // HTTP客户端
    QWebSocket            ws_client;    // WebSocket客户端
    model::DataCenter*    data_center;  // 数据中心实例
    QProtobufSerializer   serializer;   // Protobuf 序列化器
};

/// @brief 处理 HTTP 响应并返回一个共享指针对象(PB类型)。
/// @param T 要返回的共享指针所指向的对象类型(PB类型)。
/// @param response 指向 QNetworkReply 的指针，表示收到的 HTTP 响应。
/// @return 一个指向类型为 T 的对象的 std::shared_ptr。
template <typename T>
inline std::shared_ptr<T> NetClient::handle_http_response(QNetworkReply* response)
{
    /* 出错 */
    if (response->error() != QNetworkReply::NoError) {
        LOG() << "handle_http_response failed:" << response->errorString();
        response->deleteLater();
        return nullptr;
    }
    /* 拿到 body进行序列化 */
    QByteArray         response_body = response->readAll();
    std::shared_ptr<T> resp          = std::make_shared<T>();
    if (!resp->deserialize(&serializer, response_body)) {
        LOG() << "handle_http_response failed: deserialize failed";
        return nullptr;
    }
    if (resp->success() == false) {
        LOG() << "!success, why:" << resp->errmsg();
        response->deleteLater();
        return nullptr;
    }
    // LOG() << "success, response received, request ID:" << resp->requestId();
    response->deleteLater();
    return resp;
}

/// @brief 带出参返回值的handle_http_response()
template <typename T>
inline std::shared_ptr<T> NetClient::handle_http_response(QNetworkReply* response, bool* ok, QString* reason)
{
    /* 出错 */
    if (response->error() != QNetworkReply::NoError) {
        LOG() << "handle_http_response failed:" << response->errorString();
        response->deleteLater();
        *ok     = false;
        *reason = response->errorString();
        return nullptr;
    }
    /* 拿到 body进行序列化 */
    QByteArray         response_body = response->readAll();
    std::shared_ptr<T> resp          = std::make_shared<T>();
    if (!resp->deserialize(&serializer, response_body)) {
        LOG() << "handle_http_response failed: deserialize failed";
        *ok     = false;
        *reason = "deserialize failed";
        return nullptr;
    }
    if (resp->success() == false) {
        LOG() << "!success, why:" << resp->errmsg();
        *ok     = false;
        *reason = resp->errmsg();
        response->deleteLater();
        return nullptr;
    }
    *ok     = true;
    *reason = "";
    // LOG() << "success, response received, request ID:" << resp->requestId();
    response->deleteLater();
    return resp;
}